// PS3838Client.js
const axios = require('axios');
const { v4: uuidv4 } = require('uuid');
require("./config/global_config")
class PS3838Client {
    constructor({ username, password, baseURL = 'https://api.ps3838.com' }) {
        this.username = username;
        this.password = password;
        this.baseURL = baseURL;
        this.sinceMap = {}; // { [sportId]: last }
        this.oddsFormat = "HONGKONG"
        this.lastData = false
        this.sinceStore = {
            odds: {},
            fixtures: {},
        };
        this.fullOddsMap = new Map();

        this.client = axios.create({
            baseURL: this.baseURL,
            headers: {
                'Authorization': 'Basic ' + Buffer.from(`${username}:${password}`).toString('base64'),
                'Accept': 'application/json'
            }
        });
    }

    async getSports() {
        try {
            const response = await this.client.get('/v3/sports');
            return response.data;
        } catch (error) {
            global.info('getSports失败:' + error.response?.data || error.message);
        }
    }

    async getFixtures(params, isAll) {
        const { sportId } = params;
        if (isAll) {
            // 如果 isAll 为 true，则清除上次的 since 值
            this.sinceStore.fixtures[sportId] = false;
        }
        if (!sportId) throw new Error('必须提供 sportId');
        const query = {
            ...params,
            ...(this.sinceStore.fixtures[sportId] ? { since: this.sinceStore.fixtures[sportId] } : {})
        };
        try {
            const response = await this.client.get('/v3/fixtures', { params: query });
            const last = response.data?.last;
            if (typeof last === 'number') {
                this.sinceStore.fixtures[sportId] = last;
            }
            return response.data;
        } catch (error) {
            global.info('getFixtures失败:' + (error.response?.data.message));
            return false;
        }
    }

    async getLiveFixturesBySport(sportId) {
        try {
            const data = await this.getFixtures({ sportId });
            const leagues = data.league || [];

            const liveGames = leagues.flatMap(league => {
                const leagueName = league.name;
                const leagueId = league.id;
                return (league.events || [])
                    .filter(event => event.liveStatus === 1)
                    .map(event => ({
                        ...event,
                        leagueId,
                        leagueName,
                    }));
            });
            return liveGames;
        } catch (error) {
            console.error(`获取体育ID为 ${sportId} 的 live 比赛失败:`, error.response?.data || error.message);
            throw error;
        }
    }

    async parseLiveEvents(fixturesData) {
        if (!fixturesData || !Array.isArray(fixturesData.league)) return [];

        const liveEvents = [];
        const now = new Date();
        fixturesData.league.forEach(league => {
            if (!Array.isArray(league.events)) return;
            league.events.forEach(event => {
                const now = new Date();
                const startTime = new Date(event.starts);
                const gameDurationMs = 2 * 60 * 60 * 1000;
                const started = startTime <= now;
                const notEnded = now - startTime <= gameDurationMs;
                const isLiveNow = event.liveStatus === 1 && started && notEnded;
                if (isLiveNow && event.home && event.away && (event.home.indexOf("(Corners)") < 0) && event.home.indexOf("(") < 0) {
                    // console.info(`比赛 ${event.home} v ${event.away} 状态: ${event.liveStatus} | ${event.status}, 开始时间: ${startTime.toISOString()}, 当前时间: ${now.toISOString()}, 是否直播中: ${isLiveNow}`);
                    liveEvents.push({
                        eventId: event.id,
                        leagueId: league.id,
                        leagueName: league.name,
                        startTimeUTC: event.starts,
                        started,
                        homeTeam: event.home,
                        awayTeam: event.away,
                        liveStatus: event.liveStatus,
                        parentId: event.parentId,
                        betAcceptanceType: event.betAcceptanceType,
                        parlayRestriction: event.parlayRestriction,
                        altTeaser: event.altTeaser,
                        version: event.version,
                        status: event.status
                    });
                }
            });
        });
        return liveEvents;
    }

    async getOdds({ sportId, leagueIds = [], eventIds = [] }) {
        let oddsFormat = this.oddsFormat
        let query = {
            sportId, oddsFormat
        };

        if (leagueIds.length > 0) {
            query.leagueIds = leagueIds.join(',');
        }

        if (eventIds.length > 0) {
            query.eventIds = eventIds.join(',');
        }

        if (this.sinceStore.odds[sportId]) {
            query.since = this.sinceStore.odds[sportId]
        }
        try {
            const res = await this.client.get('/v3/odds', { params: query });
            const last = res.data?.last;
            if (typeof last === 'number') {
                this.sinceStore.odds[sportId] = last;
            }
            return res.data;
        } catch (error) {
            global.info(`getOdds失败:` + (error.response?.data || error.message));
            global.info(`eventIds size: ${eventIds.length}, leagueIds size: ${leagueIds.length}`);
            this.getOdds({ sportId, leagueIds, eventIds })
        }
    }

    async mergeFixturesWithOdds(fixtures, oddsData) {
        const oddsMap = new Map();
        if (!oddsData.leagues) { return [] }
        // 遍历 oddsData，提取每场比赛的盘口数据
        oddsData.leagues.forEach(league => {
            league.events.forEach(event => {
                const period = event.periods?.[0]; // 只取第一个 period
                if (!period) {
                    console.info("没有period数据，跳过", event.id);
                    return
                };

                const handap = (period.number == 0 && period.status == 1 && period.spreads || []).map(s => ({
                    eventId: event.id,
                    lineId: period.lineId,
                    altLineId: s.altLineId,
                    periodNumber: period.number,
                    max: s.max,
                    v: s.hdp.toString(),
                    a: s.home.toString(),
                    b: s.away.toString()
                }));

                const over_under = (period.number == 0 && period.status == 1 && period.totals || []).map(t => ({
                    eventId: event.id,
                    points: t.points,
                    max: t.max,
                    v: t.points.toString(),
                    a: t.over.toString(),
                    b: t.under.toString()
                }));

                const moneyline = period.number == 0 && period.status == 1 && period.moneyline && period.moneyline.draw && {
                    eventId: event.id,
                    v: period.moneyline.draw.toString(),
                    a: period.moneyline.home.toString(),
                    b: period.moneyline.away.toString()
                };

                let score = `${event.homeScore ? event.homeScore : 0}:${event.awayScore ? event.awayScore : 0}`;
                let red = `${event.homeRedCards}:${event.awayRedCards}`
                let lineId = period.lineId
                let periodNumber = period.number
                let status = period.status
                oddsMap.set(event.id, { handap, over_under, moneyline, score, red, lineId, periodNumber, status });

            });
        });

        // 合并 fixture 和对应的盘口数据
        return fixtures.map(fixture => {
            if (!oddsMap.has(fixture.eventId)) {
                return false
            }
            if (fixture.homeTeam.indexOf("(Corners)") > 0) {
                return false
            }
            if (fixture.homeTeam.indexOf("(") > 0) {
                console.info("1111")
                return false
            }
            const odds = oddsMap.get(fixture.eventId) || { handap: [], over_under: [], moneyline: false };
            // this.compareAndUpdateOdds(fixture.eventId, odds.moneyline, odds.handap, odds.over_under, fixture.homeTeam, fixture.awayTeam);
            let result = {
                id: fixture.eventId,
                home: fixture.homeTeam,
                away: fixture.awayTeam,
                league: fixture.leagueName,
                score: odds.score,
                startTime: fixture.startTimeUTC,
                handap: odds.handap,
                over_under: odds.over_under,
                moneyline: odds.moneyline,
                red: odds.red,
                lineId: odds.lineId,
                periodNumber: odds.periodNumber,
                status: odds.status
            };
            if (odds.status == 2) {
                // console.info(`${new Date()}-不可投注：${fixture.homeTeam} v ${fixture.awayTeam}，id: ${fixture.eventId}`);
            }
            return result
        }).filter(Boolean);
    }


    async placeBet(data) {
        global.info(`开始下单平博: 3`);
        let uuid = uuidv4();
        let payload = {
            "oddsFormat": this.oddsFormat,
            "uniqueRequestId": uuid,
            "acceptBetterLine": true,
            "stake": Number(data.stake),
            "winRiskStake": "RISK",
            "lineId": data.lineId,
            "altLineId": data.altLineId,
            "pitcher1MustStart": true,
            "pitcher2MustStart": true,
            "fillType": "NORMAL",
            "sportId": data.sportId,
            "eventId": data.eventId,
            "periodNumber": Number(data.periodNumber),
            "betType": data.betType,//"SPREAD",
            "team": data.team ? data.team : null, //"TEAM1" "TEAM2" "DRAW"
            "side": data.side ? data.side : null,
            "handicap": data.team == "TEAM1" ? data.handicap : -(parseFloat(data.handicap)),
        }
        global.info(`开始下单平博: 4`);
        const response = await this.client.post('/v2/bets/place', payload);
        const { status, errorCode } = response.data;
        global.info(`开始下单平博: 5  --> ${status}`);
        if (status === "ACCEPTED" || status === "PENDING_ACCEPTANCE") {
            global.info(`下注成功 ${data.eventId} ${data.team} ${data.side} ${data.handicap}  赔率: ${data.odds} 金额: ${data.stake}`);
        } else if (status === "PROCESSED_WITH_ERROR") {
            global.info(`下注失败，错误码：${errorCode} ${data.eventId} ${data.team} ${data.side} ${data.handicap}  赔率: ${data.odds} 金额: ${data.stake}`);
            switch (errorCode) {
                case "LINE_CHANGED":
                    break;
                case "SCORE_CHANGED":
                    break;
                case "RED_CARDS_CHANGED":
                    global.info("赔率已变，重新拉 getOdds 再试一次");
                    break;
                case "DUPLICATE_UNIQUE_REQUEST_ID":
                    break;
                case "INSUFFICIENT_FUNDS":
                    break;
                case "ABOVE_EVENT_MAX":
                    break;
                case "ABOVE_MAX_BET_AMOUNT":
                    break;
                case "BELOW_MIN_BET_AMOUNT":
                    global.info(`下注金额不合法或余额不足，需人工介入`);
                    break;
                case "PAST_CUTOFFTIME":
                case "OFFLINE_EVENT":
                case "ALL_BETTING_CLOSED":
                    global.info("下注窗口已关闭，终止下单");
                    break;
                case "RESUBMIT_REQUEST":
                    break;
                default:
                    global.info(`未知错误码 ${errorCode}，记录并跳过`);
                    break;
            }
        }
    }


    async compareAndUpdateOdds(eventId, moneyline, handaps, totals, home, away) {
        const map = this.fullOddsMap;
        const keyOf = (type, v) => `${eventId}_${type}_${v}`;

        const detectDrop = (oldVal, newVal) => oldVal && newVal && oldVal > 0 && ((oldVal - newVal) / oldVal >= 0.1);

        // === 胜平负 ===
        if (moneyline && moneyline.a && moneyline.b) {
            const key = keyOf("moneyline", "draw");
            const newData = { a: parseFloat(moneyline.a), b: parseFloat(moneyline.b), v: parseFloat(moneyline.v) };
            const old = map.get(key);

            if (old) {
                if (detectDrop(old.a, newData.a)) {
                    console.log(`胜平负 主队赔率下降  : ${home} v ${away}  ${old.a} => ${newData.a}`)
                };
                if (detectDrop(old.b, newData.b)) {
                    console.log(`胜平负 客队赔率下降  : ${home} v ${away}  ${old.b} => ${newData.b}`)
                };
                if (old.v && newData.v && detectDrop(old.v, newData.v)) {
                    console.log(`胜平负 平局赔率下降 : ${home} v ${away}  ${old.v} => ${newData.v}`)
                };
            }

            map.set(key, newData);
        }

        // === 让分 ===
        handaps.forEach(s => {
            const key = keyOf("handap", s.v);
            const newData = { v: s.v, a: parseFloat(s.a), b: parseFloat(s.b) };
            const old = map.get(key);

            if (old) {
                if (detectDrop(old.a, newData.a)) console.log(`让分 主队下降 >6%:${home} v ${away} ${old.a} => ${newData.a}`);
                if (detectDrop(old.b, newData.b)) console.log(`让分 客队下降 >6%:${home} v ${away}  ${old.b} => ${newData.b}`);
            }

            map.set(key, newData);
        });

        // === 大小盘 ===
        totals.forEach(t => {
            const key = keyOf("total", t.v);
            const newData = { v: t.v, a: parseFloat(t.a), b: parseFloat(t.b) };
            const old = map.get(key);

            if (old) {
                if (detectDrop(old.a, newData.a)) console.log(`大小 Over 降价  ${old.v} : ${home} v ${away}   ${old.a} => ${newData.a}`);
                if (detectDrop(old.b, newData.b)) console.log(`大小 Under 降价 ${old.v} : ${home} v ${away}  ${old.b} => ${newData.b}`);
            }

            map.set(key, newData);
        });
    }

    // 获取余额

    async getBalance() {
        try {
            const response = await this.client.get('/v1/client/balance');
            return response.data;
        } catch (error) {
            global.info('getBalance失败:' + (error.response?.data || error.message));
            return false;
        }
    }


}

module.exports = PS3838Client

// const client = new PS3838Client({
//     username: 'BIA00030TP',
//     password: 'wwss2233'
// });


// (async () => {
//     const sportId = 29; // 篮球
//     let data = { "stake": 17, "eventId": 1614747925, "lineId": 3243490183, "periodNumber": 0, "sportId": 29, "betType": "SPREAD", "handicap": "0.25", "team": "TEAM1", "side": null }
//     client.placeBet(data)

// })();
